/**
*  \file kwptprx.c
*
*  \brief
*
*  \author Michael J. Hohmann  <michael.hohmann@linde-mh.de>
*
*
@startuml
title Single Frame ( 7 or less databytes)
sender -> receiver: single frame [ (0x00 | len), d1, d2, d3, d4, d5, d6, d7]
@enduml


@startuml
title Segemented Transfer (more than 7 databytes)

sender -> receiver: first frame [(0x10 |len), len, d1, d2, d3, d4, d5, d6 ]
receiver -[#blue]> sender: flow control frame [0x30,block_size,separation_time]
sender -> receiver: consecutive frame [(0x20 | counter), d1, d2, d3, d4, d5, d6, d7 ]
sender --> receiver: block_size x consecutive frame
sender -> receiver: consecutive frame [(0x20 | counter), d1, d2, d3, d4, d5, d6, d7 ]

receiver -[#blue]> sender: flow control frame
sender -> receiver: consecutive frame [(0x20 | counter), d1, d2, d3, d4, d5, d6, d7 ]
sender --> receiver: block_size x consecutive frame
sender -> receiver: consecutive frame [(0x20 | counter), d1, d2, d3, d4, d5, d6, d7 ]
@enduml


*/

/*===============================================[ private includes  ]================================================*/
#include "kwptprx.h"
#include "../assertor/assertor.h"
#include "../pal/pal_mem.h"
#include "../cdt/cdt.h"
#include "../systime/systime.h"
/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/
enum kwptprx_state
{
   kwptprx_state_single_or_first,
   kwptprx_state_consecutive,
   kwptprx_state_msg_ready,
};


/*===============================================[ private variables ]================================================*/

/*===============================================[ private functions ]================================================*/
static BO kwptprx_state_idle(struct kwptprx_obj *me, U8 *data );
static BO kwptprx_state_wait_consecutive(struct kwptprx_obj *me, U8 *data );

/*================================================[ inline functions ]================================================*/

/*================================================[ public functions ]================================================*/

RC kwptprx_construct( struct kwptprx_obj *me, struct kwp_msg* buffer,kwptprx_transmit_func_t transmit_func, U8 block_size, U8 separation_time)
{	
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   pal_memzero( me, sizeof *me );

   me->buffer=buffer;
   me->transmit = transmit_func;

   me->block_size = block_size;
   me->separation_time=separation_time;

   return RC_SUCCESS;
}


BO kwptprx_process_msg(struct kwptprx_obj *me, U8 *data )
{
   if( me->msg_available)
   {  /* there is still an unprocess message in the buffer */
      /* ignore messages until message was released        */
      return TRUE;
   }

   if(systime_timeout(me->time_last_message, 100 * me->separation_time) )
   {  /* Last transmission was timed out
         Therefore a new first or single message is expected */
      me->expect_consecutive_frame=FALSE;
   }

   me->time_last_message=systime_get();

   if( me->expect_consecutive_frame )
   {
      return kwptprx_state_wait_consecutive( me, data );
   }

   return kwptprx_state_idle(me, data );
}

struct kwp_msg* kwptprx_take_msg(struct kwptprx_obj *me)
{
   if(me->msg_available )
   {
      return me->buffer;
   }
   return NIL;
}

void kwptprx_release_msg(struct kwptprx_obj *me)
{
   me->msg_available=FALSE;
}



/*===============================================[ private functions ]================================================*/
static BO kwptprx_state_idle(struct kwptprx_obj *me, U8 *data )
{
   switch( kwp_msg_type_get( data[0]) )
   {
      case kwptp_msg_type_single_frame:
      {
         me->buffer->len = data[0] & 0x0F; /* set message length */
         pal_memcpy(&me->buffer->data[0], &data[1], me->buffer->len );
         me->expect_consecutive_frame=FALSE;
         me->msg_available=TRUE;
         return TRUE;
      }
      case kwptp_msg_type_first_frame:
      {
         U8 flow_control_msg[8]={ 0x30,me->block_size,me->separation_time,0,0,0,0,0};
         me->buffer->len = ( (0x0F & data[0]) << 8 )| (data[1]); /* set message length */
         pal_memcpy(&me->buffer->data[0], &data[2], 6 );   /* copy data to message */
         me->expected_sequence_number = 0; /* the first consecutive frame will have this sequence number */
         me->block_size_counter = me->block_size;
         me->bytes_received=6;
         me->time_last_message=systime_get();
         me->transmit(flow_control_msg);
         me->expect_consecutive_frame=TRUE;
         me->msg_available=FALSE;
         me->p_write_data=&me->buffer->data[6];
         me->bytes_missing=me->buffer->len-6;
         return FALSE;
      }
      default:
      {
         //if( is_pending_msg() )
         //{
         //   me->timeout_timer=me->timeout;
         //}
         me->msg_available=FALSE;
         me->expect_consecutive_frame=FALSE;
         return FALSE;
       }
   }
}



static BO kwptprx_state_wait_consecutive(struct kwptprx_obj *me, U8 *data )
{
   if ( kwptp_msg_type_consecutive_frame != kwp_msg_type_get( data[0] ) )
   {  /* unexpected message - transmission was aborted and this could be a valid new request */
      return kwptprx_state_idle(me, data );
   }

   { /* check sequence number */
      //U8  sequence_number = data[0] & 0x0F;
      /* test sequence number */
      me->expected_sequence_number++;
      if( (data[0] & 0x0F) != (me->expected_sequence_number&0x0F) )
      {
          me->expect_consecutive_frame=FALSE;
          me->msg_available=FALSE;
          return FALSE;
      }
   }


   {  /* copy the data to buffer */
      U16 bytes_in_this_frame = 7;

      if( me->bytes_missing < bytes_in_this_frame )
      {
         bytes_in_this_frame = me->bytes_missing;
      }

      pal_memcpy(me->p_write_data, &data[1], bytes_in_this_frame );/* copy data to message */

      me->p_write_data  += bytes_in_this_frame;
      me->bytes_missing -= bytes_in_this_frame;
   }

   if( 0 == me->bytes_missing )
   {/* do we received all data */
       me->expect_consecutive_frame=FALSE;
       me->msg_available=TRUE;
       return TRUE;
   }

   /* send flow control if needed */
   if( me->block_size !=0 )
   {
      if( CDT( &me->block_size_counter ))
      {
         U8 flow_control_msg[8]={ 0x30,me->block_size,me->separation_time,0,0,0,0,0};
         me->block_size_counter = me->block_size;/* reload */
         me->expect_consecutive_frame=TRUE;
         me->msg_available=FALSE;
         me->transmit( flow_control_msg );
         return FALSE;
      }
   }

   me->expect_consecutive_frame=TRUE;
   me->msg_available=FALSE;
   return FALSE;
}

/***********************************************************************************************************************
*                             Copyright 2020 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
